/* reading from the same location to different destination locations
 */


#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <omp.h>

#include "defines.h"
#include "papi_test.h"

long *src;
long result;
unsigned long size;

void pranith_start() {printf("");}
void pranith_stop() {printf("");}
void recordTime(struct timeval* current)
{
    gettimeofday(current, NULL);

    return;
}

float diffTime(struct timeval before, struct timeval after)
{
    float time_sec = ((after.tv_sec - before.tv_sec) + ((after.tv_usec - before.tv_usec) / 1000000.0));

    return time_sec;
}

void warmup()
{
  unsigned long j;
  size = 256 * 1024 * 1024 / sizeof(long); // 256 MB
  src = (long *)malloc(sizeof(long) * size);
  //initialize array
  for (j = 0; j < size; j++)
    src[j] = 8;
}

const int eventlist[] = {
    PAPI_L1_DCA,
    PAPI_L1_TCM,
    PAPI_L2_DCA,
    PAPI_L2_TCM,
    PAPI_L3_DCA,
    PAPI_L3_TCM,
    PAPI_TOT_CYC,
    0
};

void Thread(num_iter)
{
  unsigned int j, i = 0, k;
  volatile long dest;
  int retval;
  int EventSet = PAPI_NULL;
  long long values = 0;
  double final;
  PAPI_event_info_t evinfo;
  char event_name[PAPI_MAX_STR_LEN];

  int tid = omp_get_thread_num();
  if ( ( retval = PAPI_create_eventset( &EventSet ) ) != PAPI_OK )
    test_fail( __FILE__, __LINE__, "PAPI_create_eventset", retval );
  for (j = 0; eventlist[j] != 0; j++)
  {
    retval = PAPI_event_code_to_name( eventlist[j], event_name );

    if ( retval != PAPI_OK )
      test_fail( __FILE__, __LINE__, "PAPI_event_code_to_name", retval );

    if ( (retval = PAPI_add_event( EventSet, eventlist[j] ) ) != PAPI_OK )
      continue;

    if ( (retval = PAPI_get_event_info( eventlist[j], &evinfo )) != PAPI_OK )
      test_fail( __FILE__, __LINE__, "PAPI_get_event_info", retval );

    if (tid == 1000)
        printf( "\nEvent: %s\nShort: %s\nLong: %s\n\n",
                evinfo.symbol, evinfo.short_descr, evinfo.long_descr );

    PAPI_start(EventSet);
    if ( ( retval = PAPI_reset( EventSet ) ) != PAPI_OK )
      test_fail( __FILE__, __LINE__, "PAPI_reset", retval );

    for(k = 0; k < num_iter; k++)
    {

      /* make subsequent iterations dependent on previous iterations */

      if (i+indexarr399 >= size)
        i = 0;

      final += dest;
    }

    if ( ( retval = PAPI_read( EventSet, &values ) ) != PAPI_OK )
      test_fail( __FILE__, __LINE__, "PAPI_read", retval );

    PAPI_stop(EventSet, NULL);
    if ( ( retval =
          PAPI_remove_event( EventSet, eventlist[j] ) ) != PAPI_OK )
      test_fail( __FILE__, __LINE__, "PAPI_remove_event", retval );

    double res = values;
    //printf("\nThread tid : %d, %s, %e", omp_get_thread_num(), event_name, res);
    if (tid == 0)
        printf("%e\t", res);
  }

  result = final;

  PAPI_unregister_thread();
  return;
}

int main(int argc, char* argv[])
{
  warmup();
  pranith_start();
  int num_mem_ops = 1, i = 0;
  int retval;

  unsigned long num_iter = 8000000 / num_mem_ops;
  struct timeval before, after;

  int num_threads = omp_get_max_threads();

  retval = PAPI_library_init(PAPI_VER_CURRENT);
  if ( retval != PAPI_VER_CURRENT )
    test_fail( __FILE__, __LINE__, "PAPI_library_init", retval );

  retval =
    PAPI_thread_init( ( unsigned
          long ( * )( void ) ) ( omp_get_thread_num ) );
  if ( retval != PAPI_OK ) {
    if ( retval == PAPI_ESBSTR )
      test_skip( __FILE__, __LINE__, "PAPI_thread_init", retval );
    else
      test_fail( __FILE__, __LINE__, "PAPI_thread_init", retval );
  }

  recordTime(&before);
#pragma omp parallel for schedule(static)
  for (i = 0; i < num_threads; i++)
    Thread(num_iter/num_threads);
  recordTime(&after);

  printf("%f\n", diffTime(before, after));
  //printf("num iter %d M\n", (int) (num_iter / 1000000));
  pranith_stop();

  fflush(NULL);
  return 0;
}
